package controllers;

import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.UntypedActor;
import models.crawlers.Invask;
import models.crawlers.Muztorg;
import models.crawlers.Slami;
import models.crawlers.common.ShopCrawler;
import models.crawlers.common.WebSiteInfo;
import models.crawlers.entities.ProductEntity;
import org.codehaus.jackson.JsonNode;
import play.Logger;
import play.libs.Akka;
import play.libs.Comet;
import play.libs.F;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Crawler extends Controller {
    public static WebSiteInfo lastWebSiteInfo;
    public static String lastCrawler;
    final static ActorRef crawlerActor = CrawlerActor.instance;
    public static Result getInfo() {
        if (!isCrawlerActive()) {
            if (lastWebSiteInfo != null) {
                //TODO: show it
            }
            return ok(views.html.Crawler.info.render());
        } else {
            return ok(views.html.Crawler.status.render(lastCrawler));
        }
    }

    public static Result parse() throws Exception {
        Map<String,String[]> body = request().body().asFormUrlEncoded();
        String[] url = body.get("url");
        if (url.length > 0){
            ShopCrawler crawler = getCrawlerByUrl(url[0]);
            ProductEntity product = crawler.getProduct(url[0]);
        }
        return null;
    }

    private static ShopCrawler getCrawlerByUrl(String url) throws IOException {
        if (url.contains("muztorg.ru")){
            return new Muztorg();
        } else if (url.contains("slami.ru")){
            return new Slami();
        } else if (url.contains("invask.ru")) {
            return new Invask();
        }
        return null;
    }
    public static class CrawlerActor extends UntypedActor {
        static ActorRef instance = Akka.system().actorOf(new Props(CrawlerActor.class));
        public static boolean isRunning;
        private static ShopCrawler crawlerInstance;
        List<Comet> sockets = new ArrayList<Comet>();
        @Override
        public void onReceive(Object message) throws Exception {
            handleConnections(message);
            handleMessages(message);
        }
        private void handleMessages(Object message) {
            if (message instanceof ShopCrawler){
                crawlerInstance = (ShopCrawler) message;
                isRunning = true;
                Observer observer = new Observer() {
                    public void update(Observable observable, Object eventObject) {
                        if (eventObject instanceof WebSiteInfo) {
                            lastWebSiteInfo = (WebSiteInfo) eventObject;
                        }
                        JsonNode json = Json.toJson(eventObject);
                        for(Comet cometSocket: sockets) {
                            cometSocket.sendMessage(json);
                        }
                    }
                };
                if (crawlerInstance != null) {
                    crawlerInstance.deleteObservers();
                    crawlerInstance.addObserver(observer);
                }
                ExecutorService exec = Executors.newSingleThreadExecutor();
                exec.submit(crawlerInstance);

            }
            if("STOP".equals(message)) {
                if (crawlerInstance != null){
                    crawlerInstance.stop();
                    isRunning = false;
                }
            }
        }
        private void handleConnections(Object message) {
            if(message instanceof Comet) {
                final Comet cometSocket = (Comet)message;
                if(sockets.contains(cometSocket)) {
                    // Brower is disconnected
                    sockets.remove(cometSocket);
                    Logger.info("Browser disconnected (" + sockets.size() + " browsers currently connected)");
                } else {
                    // Register disconnected callback
                    cometSocket.onDisconnected(new F.Callback0() {
                        public void invoke() {
                            getContext().self().tell(cometSocket);
                        }
                    });
                    // New browser connected
                    sockets.add(cometSocket);
                    Logger.info("New browser connected (" + sockets.size() + " browsers currently connected)");
                }
            }
        }

    }
    public static Result getStatus() {
        return ok(new Comet("parent.statusInfo") {
            @Override
            public void onConnected() {
                crawlerActor.tell(this);
            }
        });
    }

    public static Result stop() {
        crawlerActor.tell("STOP");
        lastCrawler = null;
        return redirect(routes.Crawler.getInfo());
    }

    private static boolean isCrawlerActive() {
        return lastCrawler != null;
    }

    public static Result start(String crawlerName) throws Exception {
        if (!isCrawlerActive()) {
            ShopCrawler localInstance = getCrawlerInstance(crawlerName);
            lastCrawler = localInstance.getClass().getSimpleName();
            crawlerActor.tell(localInstance);
        }
        return redirect(routes.Crawler.getInfo());
    }

    private static ShopCrawler getCrawlerInstance(String crawlerName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        lastCrawler = crawlerName;
        return (ShopCrawler) Class.forName(String.format("models.crawlers.%1$s", crawlerName)).newInstance();
    }
}
